package reesoft.certificate;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reesoft.common.code.ErrorCode;
import reesoft.common.entity.Result;
import reesoft.common.util.EncryptUtil;
import reesoft.common.util.StringUtil;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 密钥管理类
 *
 * Created by reesoft on 2017-08-11
 */
@Service
public class SecretKeyManager implements InitializingBean
{
    private static final Logger LOG = LoggerFactory.getLogger(SecretKeyManager.class);

    private static final Map<String, String> secretKeys = new ConcurrentHashMap<>();

    @Autowired
    private CertificateConfig certificateConfig;

    @Override
    public void afterPropertiesSet() throws Exception
    {
        if (!StringUtil.isEmpty(certificateConfig.getSecretKeyFilePath()))
        {
            preloadKeysFromFile(certificateConfig.getSecretKeyFilePath());
        }
    }

    // 从配置文件加载密钥
    public int preloadKeysFromFile(String configFilePath)
    {
        LOG.debug("preloadKeysFromFile, configFilePath:" + configFilePath);

        if (StringUtil.isBlank(configFilePath))
        {
            return -1;
        }

        Properties properties = new Properties();
        try
        {
            properties.load(new FileInputStream(configFilePath));

            return preloadKeys(properties);
        }
        catch (IOException e)
        {
            LOG.error("preloadKeysFromFile exception:", e);
            return -1;
        }
    }

    // 根据配置文件加载密钥
    private int preloadKeys(Properties properties)
    {
        final Enumeration<String> propertyNames = (Enumeration<String>) properties.propertyNames();
        int loadKeys = 0;

        while (propertyNames.hasMoreElements())
        {
            String propertyName = propertyNames.nextElement();
            String propertyValue = properties.getProperty(propertyName, "");

            if (propertyName.indexOf(".secret") > 0)
            {
                addKey(propertyName.substring(0, propertyName.length() - 7), propertyValue);
                loadKeys++;
            }
        }

        LOG.info(String.valueOf(loadKeys) + " keys loaded");

        return loadKeys;
    }

    public boolean addKey(String id, String key)
    {
        if (id == null || key == null)
        {
            return false;
        }

        LOG.debug("addKey, id:" + id);

        if (secretKeys.get(id) != null)
        {
            LOG.warn("Secret key " + id + " already exists and is replaced");

            secretKeys.remove(id);
        }

        secretKeys.put(id, key);

        return true;
    }

    public boolean removeKey(String id)
    {
        if (id == null)
        {
            return false;
        }

        LOG.debug("removeKey, id:" + id);

        if (secretKeys.get(id) != null)
        {
            secretKeys.remove(id);

            return true;
        }
        else
        {
            return false;
        }
    }

    // 释放内存中所有的密钥
    public void freeAllKeys()
    {
        LOG.info("freeAllKeys");

        secretKeys.clear();
    }

    // 给数据签名
    public Result<String> signData(String id, String data)
    {
        if (id == null || data == null)
        {
            return new Result<>(ErrorCode.INVALID_PARAMETER);
        }

        LOG.debug("signData, id:" + id);

        String secretKey = getKey(id);

        if (!StringUtil.isEmpty(secretKey))
        {
            String signature = EncryptUtil.calculateMd5(secretKey + data);
            return new Result<>(ErrorCode.SUCCESS, signature);
        }
        else
        {
            return new Result<>(CertificateErrorCode.KEY_NOT_EXISTS);
        }
    }

    // 验证数据的签名
    public Result verifySignData(String id, String data, String signature)
    {
        LOG.debug("verifySignData, id:" + id);

        Result<String> signResult = signData(id, data);

        if (ErrorCode.SUCCESS.equals(signResult.getCode()))
        {
            if (signResult.getData().equals(signature))
            {
                return new Result(ErrorCode.SUCCESS);
            }
            else
            {
                return new Result(ErrorCode.INVALID_DATA_SIGNATURE);
            }
        }
        else
        {
            return signResult;
        }
    }

    private String getKey(String id)
    {
        return secretKeys.get(id);
    }
}
